﻿Imports System.Text


Public Structure LR1Item
    Implements IEquatable(Of LR1Item)

    Private ReadOnly m_productionIndex As Integer
    Public ReadOnly Property ProductionIndex() As Integer
        Get
            Return m_productionIndex
        End Get
    End Property

    Private ReadOnly m_rightHandPosition As Integer
    Public ReadOnly Property RightHandPosition() As Integer
        Get
            Return m_rightHandPosition
        End Get
    End Property

    Private ReadOnly m_lookAheadIndex As Integer
    Public ReadOnly Property LookAheadTerminalIndex() As Integer
        Get
            Return m_lookAheadIndex
        End Get
    End Property

    Public Sub New(ByVal productionIndex As Integer, ByVal rightHandPosition As Integer, ByVal lookAheadTerminalIndex As Integer)
        m_productionIndex = productionIndex
        m_rightHandPosition = rightHandPosition
        m_lookAheadIndex = lookAheadTerminalIndex
    End Sub

    Public Overrides Function Equals(ByVal obj As Object) As Boolean

        If obj Is Nothing OrElse (Not TypeOf obj Is LR1Item) Then Return False
        Dim other = DirectCast(obj, LR1Item)

        Return Equals(other)
    End Function

    Public Overrides Function GetHashCode() As Integer
        'Return m_productionIndex + 2 Xor m_rightHandPosition + 3 'LALR(1) version

        Return m_lookAheadIndex + 1 Xor m_productionIndex + 2 Xor m_rightHandPosition + 3 'LR(1) version
    End Function

    'Public Shared Function GetClosure(ByVal items As IEnumerable(Of LR1Item)) As IEnumerable(Of LR1Item)

    '    If items Is Nothing Then
    '        Throw New ArgumentNullException("items")
    '    End If

    '    'Closure(I) =        
    '    ' repeat                                
    '    '  for any item (A → α.Xβ, z) in I     
    '    '     for any production X → γ         
    '    '         for any w ∈ FIRST(βz)        
    '    '           I ← I ∪ {(X → .γ, w)}
    '    ' until I does not change
    '    ' return I                              
    '    Dim closure As New HashSet(Of LR1Item)(items)
    '    Dim lastAddedItems = closure.ToList()
    '    Dim newAddedItems As List(Of LR1Item)
    '    Dim lastClosureCount As Integer
    '    Do
    '        newAddedItems = New List(Of LR1Item)
    '        For Each item In lastAddedItems
    '            If item.RightHandPosition = item.Production.Sequence.Count Then Continue For
    '            Dim currentProductionVar = TryCast(item.Production.Sequence(item.RightHandPosition), ProductionVariable)
    '            If currentProductionVar Is Nothing Then Continue For

    '            For Each p In currentProductionVar.Productions
    '                Dim after As New List(Of ProductionSymbol)
    '                For i = item.RightHandPosition + 1 To item.Production.Sequence.Count - 1
    '                    after.Add(item.Production.Sequence(i))
    '                Next

    '                'count FIRST(after + item.LookAhead)
    '                after.Add(item.LookAhead)
    '                Dim first As New HashSet(Of Terminal)

    '                For Each symbol In after
    '                    first.UnionWith(symbol.First)
    '                    If Not symbol.IsNullable Then Exit For
    '                Next

    '                For Each t In first
    '                    newAddedItems.Add(New LR1Item(p, 0, t))
    '                Next

    '            Next 'For Each p In currentProductionVar.Productions
    '        Next 'For Each item In lastClosure

    '        lastClosureCount = closure.Count
    '        closure.UnionWith(newAddedItems)
    '        lastAddedItems = newAddedItems
    '    Loop While lastClosureCount <> closure.Count

    '    Return closure
    'End Function

    'Public Shared Function GotoSymbol(ByVal items As IEnumerable(Of LR1Item), ByVal s As ProductionSymbol) As IEnumerable(Of LR1Item)

    '    If items Is Nothing Then
    '        Throw New ArgumentNullException("items")
    '    End If

    '    'Goto(I, X) =
    '    '    set J to the empty set
    '    '    for any item A → α:Xβ in I
    '    '        add A → αX.β to J
    '    '    return Closure(J)
    '    Dim result As New List(Of LR1Item)
    '    For Each item In items
    '        'get the "dot" position symbol
    '        If item.IsPassed Then Continue For
    '        Dim current = item.GetCurrentSymbol()
    '        If current.Equals(s) Then
    '            'move one step next
    '            result.Add(New LR1Item(item.m_production, item.m_rightHandPosition + 1, item.m_lookAhead))
    '        End If
    '    Next

    '    Return GetClosure(result)
    'End Function

    Public Overloads Function Equals(ByVal other As LR1Item) As Boolean Implements IEquatable(Of LR1Item).Equals
        If other.m_productionIndex = Me.m_productionIndex AndAlso _
            other.m_rightHandPosition = Me.m_rightHandPosition AndAlso _
            other.m_lookAheadIndex = Me.m_lookAheadIndex Then 'LR(1) version
            Return True
            'If other.m_productionIndex = Me.m_productionIndex AndAlso _
            'other.m_rightHandPosition = Me.m_rightHandPosition Then ' LALR(1) version
            '    Return True
        Else
            Return False
        End If
    End Function
End Structure

<DebuggerDisplay("{ToString()}")> _
Friend Class LR1ItemInfo
    Private m_manager As ProductionManager
    Private m_item As LR1Item

    Public Sub New(ByVal manager As ProductionManager, ByVal item As LR1Item)
        Contract.ArgumentNotNull(manager, "manager")

        m_manager = manager
        m_item = item
    End Sub

    Public ReadOnly Property Production() As Production
        Get
            Return m_manager.GetProduction(m_item.ProductionIndex)
        End Get
    End Property

    Public ReadOnly Property RightHandPostion() As Integer
        Get
            Return m_item.RightHandPosition
        End Get
    End Property

    Public ReadOnly Property LookAhead() As Terminal
        Get
            Return m_manager.GetTerminal(m_item.LookAheadTerminalIndex)
        End Get
    End Property

    Public Overrides Function ToString() As String
        Dim pattern = "{0} ->{1} " & vbTab & vbTab & "; {2}"
        Dim rightStringBuilder As New StringBuilder
        For i = 0 To RightHandPostion - 1
            rightStringBuilder.Append(" "c)
            rightStringBuilder.Append(Production.Sequence(i).ToString())
        Next
        rightStringBuilder.Append("."c)
        For i = RightHandPostion To Production.Sequence.Count - 1
            rightStringBuilder.Append(" "c)
            rightStringBuilder.Append(Production.Sequence(i).ToString())
        Next

        Return String.Format(pattern, Production.NonTerminal.ToString, rightStringBuilder.ToString, LookAhead.ToString)
    End Function

    Public Function GetCurrentSymbol() As ProductionSymbol
        If RightHandPostion < Production.Sequence.Count Then
            Return Production.Sequence(RightHandPostion)
        Else
            Return Nothing
        End If
    End Function

    Public ReadOnly Property IsPassed() As Boolean
        Get
            Return RightHandPostion = Production.Sequence.Count
        End Get
    End Property
End Class